package chipwits.operator;

import chipwits.MainPanel;
import chipwits.dnd.TransferableData;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;

/**
 *
 * @author Emmanuel
 */
public class Op_case extends Operator implements MouseListener {

    public static javax.swing.JPanel panel;
    public static Op_case opSelect = null;     //l'opérateur qui est sélectionné
    public Arrow[] arrows;
    public int xpos, ypos;  //position de l'opérateur en X et Y sur le panel

    //*********************   Constructeurs   **************************
    public Op_case(int xpos, int ypos) {   //initialise type à VIDE
        super();
        this.type = new OpType(OpType.Type.VIDE);
        this.setIcon(null);

        this.xpos = xpos;
        this.ypos = ypos;

        arrows = new Arrow[4];
        arrows[Arrow.Direction.HAUT.ordinal()] = new Arrow(xpos + ICON_WIDTH / 2 - Arrow.ARROW_WIDTH, ypos, Arrow.Direction.HAUT, false, false);   // int x, int y, int length, int width, Direction dir, setActive, type
        arrows[Arrow.Direction.DROITE.ordinal()] = new Arrow(xpos + ICON_WIDTH, ypos + ICON_HEIGHT / 2 - Arrow.ARROW_WIDTH, Arrow.Direction.DROITE, false, true);
        arrows[Arrow.Direction.BAS.ordinal()] = new Arrow(xpos + ICON_WIDTH / 2 + Arrow.ARROW_WIDTH, ypos + ICON_HEIGHT, Arrow.Direction.BAS, false, false);   //en fonction du type d'opérateur
        arrows[Arrow.Direction.GAUCHE.ordinal()] = new Arrow(xpos, ypos + ICON_HEIGHT / 2 + Arrow.ARROW_WIDTH, Arrow.Direction.GAUCHE, false, false);

        this.addMouseListener(this);        //les opDeBase n'ont pas de mouseListener

        dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, this);

    }

    public void execute() {      //fonction qui exécute l'action de l'opérateur selon son type
        switch (this.type.getType()) {
            case VIDE:
                break;
            case VOIR:
                break;

            default:
                break;
        }
    }

    public void empty() {       //vide l'opérateur.
        if (opSelect != null && opSelect == this) {
            deselect();
        }
        this.type = new OpType(OpType.Type.VIDE);
        this.setIcon(null);
        updatePanel();
    }
    
    public void changeType(OpType type, Arrow[] arrows) {
        this.type = new OpType(type.getType());
        this.setIcon(new ImageIcon(this.type.getImage()));

        if (type.getNbArrows() >= 0) {
            if (type.getNbArrows() >= 1) {
                if (this.getArrowOfType(true) == null) {
                    this.setArrow(true, Arrow.Direction.DROITE);
                }
                if (type.getNbArrows() == 2) {
                    if (this.getArrowOfType(false) == null) {
                        this.setArrow(false, Arrow.Direction.BAS);
                    }
                }
            }
        }

        for (int i = 0; i < 4; i++) {
            if (arrows == null || arrows[i] == null) {    //si c'est null, c'est que la source n'avait pas de flèches, c'était donc un opérateur de base.
                continue;
            }
            this.arrows[i].setActive(arrows[i].isActive());
            this.arrows[i].setType(arrows[i].getType());
        }
        redrawArrows();
        updatePanel();
    }

    public void select() {           //faire vérif si type != VIDE ??? (pr pas sélectionner op vide)
        if (opSelect != null) {
            opSelect.deselect();//selectArrows(false);
        }
        opSelect = this;
        setBorder(javax.swing.BorderFactory.createLineBorder(new Color(1f, 0.4f, 0f, 0.7f), 2));
        selectArrows(true);
    }

    private void deselect() {     //désélectionne l'opérateur
        if (opSelect != null) {
            opSelect.selectArrows(false);
            opSelect.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        }
        opSelect = null;
    }

    // <editor-fold defaultstate="collapsed" desc="Gestion de l'affichage">
    public static void updatePanel() {
        ((MainPanel) panel).update();
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Manip d'Images">
    /**
     * Transformation objet Image vers BufferedImage
     * @param image
     * @return
     */
    public static BufferedImage toBufferedImage(Image image) {
        /** On test si l'image n'est pas déja une instance de BufferedImage */
        if (image instanceof BufferedImage) {
            return ((BufferedImage) image);
        } else {
            image = new ImageIcon(image).getImage();
            /** On crée la nouvelle image */
            BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);

            Graphics g = bufferedImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();

            return (bufferedImage);
        }
    }

    /**
     * Colle deux images Image.
     * @param img1a
     * @param img2a
     * @return
     */
    public static Image combineImage(Image img1a, Image img2a) {
        BufferedImage img1 = toBufferedImage(img1a);
        BufferedImage img2 = toBufferedImage(img2a);
        BufferedImage combined = new BufferedImage(img1.getWidth(), img1.getHeight() / 2 + img2.getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = combined.getGraphics();
        g.drawImage(img1, 0, 0, null);
        g.drawImage(img2, 0, img1.getHeight() / 2, null);
        g.dispose();
        //ImageIO.write(combined, "png", new File("caca.png"));
        return combined;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Evenements souris">
    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() >= 2) {    //pour gérer le double clic
            if (this.type.getType() != OpType.Type.VIDE) {
                java.awt.EventQueue.invokeLater(new Runnable() {

                    public void run() {
                        new ConfigFrame().setVisible(true);
                    }
                });
            }
        }
    }

    public void mousePressed(MouseEvent e) {
        if (SwingUtilities.isLeftMouseButton(e)) {
            if (opSelect != null) {
                deselect();
            }
            if (!this.type.isVIDE()) {
                select();
            }
            updatePanel();
        } else if (SwingUtilities.isMiddleMouseButton(e)) {
            // Bouton du MILIEU
        } else if (SwingUtilities.isRightMouseButton(e)) {
            // Bouton DROIT
        }
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Gestion des flèches">
    public Arrow getArrowOfType(boolean type) {  //retourne la fleche du type entre parenthèse (verte ou rouge), null si elle existe po
        for (int i = 0; i < 4; i++) {
            if (this.arrows[i].isActive() && this.arrows[i].getType() == type) {
                return this.arrows[i];
            }
        }
        return null;
    }

    public void selectArrows(boolean select) {
        for (int i = 0; i < 4; i++) {
            opSelect.arrows[i].setSelect(select);
        }
    }

    public void redrawArrows() {
        for (int i = 0; i < 4; i++) {
            arrows[i].redraw();
        }
    }

    public void desactivArrows() {
        for (int i = 0; i < 4; i++) {
            arrows[i].setActive(false);
        }
    }

    public void setArrow(boolean type, Arrow.Direction dir) {   //Met l'arrow de type type dans la direction dir, si c'est permis.
        if(this.type.getNbArrows()==0) return;
        if (type == false && this.type.getNbArrows()==1){ //on peut mettre une fleche false que si l'opérateur le permet.
            return;
        }

        Arrow temp = null;
        while ((temp = getArrowOfType(type)) != null) {     //on désactive les fleches du type
            temp.setActive(false);
        }

        if (this.arrows[dir.ordinal()].isActive() && this.arrows[dir.ordinal()].getType() != type) {
            if(this.type.getNbArrows()==2){
                this.arrows[(dir.ordinal() + 1) % 4].setActive(true);
            }
            this.arrows[(dir.ordinal() + 1) % 4].setType(!type);
        }

        this.arrows[(dir.ordinal())].setActive(true);
        this.arrows[dir.ordinal()].setType(type);
    }
    // </editor-fold> 

    // <editor-fold defaultstate="collapsed" desc="Drag and drop">
    @Override
    public void dragDropEnd(DragSourceDropEvent evt) {
        if (evt.getDropSuccess()) {
            if ((evt.getDropAction() == DnDConstants.ACTION_MOVE)) {    //Si c'était un move (et que le DnD a réussi), on devient VIDE.
                type = new OpType(OpType.Type.VIDE);
                this.setIcon(null);
            }
            if(opSelect!=null && opSelect.equals(this)) this.deselect();
            updatePanel();
        }
    }

    @Override
    public void dragGestureRecognized(DragGestureEvent evt) {
        if (this.type.getType() != OpType.Type.POUBELLE && this.type.getType() != OpType.Type.VIDE) {     //si c'est autre chose qu'un vide ou une poubelle, on démarre le drag&drop
            Transferable t = new TransferableData(type, (ImageIcon) this.getIcon(), this.arrows);
            dragSource.startDrag(evt, DragSource.DefaultCopyNoDrop, t, this);   //start avec curseur NoDrop
        }
    }
    // </editor-fold>
}
